﻿using System;
using System.Collections.Generic;
using OdsServer.Common;
using OdsServer.Common.Enum;
using OdsServer.Common.Utils;

namespace OdsServer.Device.Core
{
    public class DriverInterop: IDriverInterop
    {
        private List<DEVLIST> deviceList            = null;
        private IntPtr deviceHandle                 = IntPtr.Zero;
        private volatile int selectedDeviceNumber   = -1;

        public State State
        {
            get 
            {
                if (this.deviceHandle != IntPtr.Zero)
                    return State.InProgress;
                else
                    return State.Closed;
            }
        }

        public bool IsReady
        {
            get { return this.deviceHandle != IntPtr.Zero; }
        }

        public IntPtr Handle
        {
            get { return this.deviceHandle; }
        }

        private int SelectedDeviceNumber
        {
            get { return this.selectedDeviceNumber; }
        }

        private unsafe struct Buffers
        {
            // used while fetching info from device select window
            public fixed char infoBuffer[2048]; // TODO: verify buffer size
        }

        ~DriverInterop()
        {
            this.CloseDevice();
        }

        public void InitDeviceList()
        {
            try
            {
                unsafe
                {
                    DEVLIST dev = new DEVLIST();
                    short max   = -1;

                    int p = DeviceDriverAdv.DRV_DeviceGetList(
                            new IntPtr(&dev),
                            ADSCONST.MaxDev,
                            new IntPtr(&max));

                    this.deviceList = new List<DEVLIST>(max);
                    this.deviceList.Add(dev);

                    // TODO: fill the rest of list
                }
            }
            catch (DllNotFoundException)
            {
                // handle application termination
            }
            catch (Exception)
            {
                // force log and quit
            }
            finally
            {
                // check for device handle 
            }
        }

        public void SelectDevice(IntPtr hWnd)
        {
            Int32 selectedDevice = 0;

            unsafe
            {
                Buffers buf = new Buffers();

                try
                {
                    DeviceDriverAdv.DRV_SelectDevice(
                            hWnd,
                            false,
                            new IntPtr(&selectedDevice),
                            new IntPtr(buf.infoBuffer));
                }
                catch (Exception)
                {
                    throw new DeviceException("Unable to select device.");
                }
            }
            this.selectedDeviceNumber = selectedDevice;
        }

        public void OpenDevice()
        {
            lock (this)
            {
                if (!this.IsReady)
                {
                    unsafe
                    {
                        Int32 handle = 0;
                        try
                        {
                            int result = DeviceDriverAdv.DRV_DeviceOpen(
                                    this.selectedDeviceNumber,
                                    new IntPtr(&handle));
                        }
                        catch
                        {
                            throw new DeviceException("Error opening device");
                        }
                        this.deviceHandle = (IntPtr)handle;
                    }

                    if (this.deviceHandle == IntPtr.Zero)
                    {
                        throw new DeviceException("Unable to open device.");
                    }
                }
                else
                    throw new DeviceException("Another device is allready opened.");
            }
        }

        public void CloseDevice()
        {
            lock (this)
            {
                if (this.IsReady)
                {
                    try
                    {
                        int result = DeviceDriverAdv.DRV_DeviceClose(
                                this.deviceHandle);
                    }
                    catch (Exception)
                    {
                        throw new DeviceException("Unable to close device.");
                    }
                    finally
                    {
                        this.deviceHandle = IntPtr.Zero;
                    }
                }
            }
        }

        public string GetDeviceName(int deviceNumber)
        {
            if (deviceNumber > -1
                    && this.deviceList != null
                    && this.deviceList.Count > deviceNumber)
            {
                DEVLIST dev = this.deviceList[deviceNumber];

                unsafe
                {
                    return ConverterUtils.ByteArrayToUtf8String(dev.szDeviceName, 50);
                }
            }
            else
                throw new DeviceException("Unable to find device by number specified.");
        }
    }
}
